/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <stdarg.h>
#include "win.h"
#include "device.h"

#define MAIN_WIN_TITLE 		"--=[Main Debug Output]"
#define TUNER_WINDOW_SZ		22


static const char *ACTIVE_LIST[6] =
{
    "-",
    "\\",
    "|",
    "-",
    "/",
    "|",
};


int generic_vprintw(DEVICE_STRUCT *dev, const char *format, va_list ap)
{
	if (dev && dev->log)
		utils_log_vprint(dev->log, format, ap);
	return 0;
}

void win_screen_update_all(NCURSES_SCREEN *screen)
{
	if (screen)
	{
//		TUNER_STRUCT * tuner = NULL;
		int i;

		pthread_mutex_lock(&screen->screen_mutex);
		wrefresh(screen->main_window->handle);
		for(i=0; i < screen->child_count; i++)
		{
			wrefresh(screen->child_window[i]->handle);
		}
		refresh();
		pthread_mutex_unlock(&screen->screen_mutex);
	}
}

void _win_vprintf(NCURSES_WINDOW * win, const char *format, va_list ap)
{
	if (win)
	{
		vwprintw(win->handle, format, ap);
		wrefresh(win->handle);
	}
}

void _win_printf(NCURSES_WINDOW * win, const char *format, ...)
{
	if (win)
	{
		va_list ap;
		va_start(ap, format);
		_win_vprintf(win, format, ap);
		va_end(ap);
	}
}


void win_tuner_write_status(TUNER_WRITEPASS_STRUCT *writepass)
{
	if (writepass && writepass->tuner && writepass->tuner->parent)
	{
		TUNER_STRUCT *tuner = writepass->tuner;
//		DEVICE_STRUCT *device = tuner->parent;
		NCURSES_WINDOW *tuner_window = NULL;
		NCURSES_SCREEN *screen  = (NCURSES_SCREEN *)tuner->screen_var;
		NCURSES_WINDOW_OPTS *opts = NULL;

		if (screen && tuner->extra_data[2])
		{
			int i, start_line=0, q;
			double rate;
			tuner_window = tuner->extra_data[2];
			opts = &tuner_window->opts;

			pthread_mutex_lock(&screen->screen_mutex);
//			pthread_mutex_lock(&tuner_window->update_lock);

//			wprintw(screen->main_window->handle, "1\n");
			if (writepass->write_status == TUNER_WRITE_STATUS_INIT)
			{
				for(i=0; i < 10; i++)
				{
					if (tuner_window->used[i] == writepass->thread_index)
						break;
				}

				if (i >= 10)
				{
					int j = 0;
					while (tuner_window->used[j])
						j++;

					tuner_window->used[j] = writepass->thread_index;
//					i = j;
				}
			}
//			wprintw(screen->main_window->handle, "2\n");

			for(i=0; i < 10; i++)
			{
				if (tuner_window->used[i] == writepass->thread_index)
					break;
			}

//			start_line = (writepass->thread_index * 4);
			start_line = 4 * (i+1);
//			wprintw(screen->main_window->handle, "3\n");

			for(q=start_line; q < start_line + 4; q++)
			{
				wmove(tuner_window->handle, q, 0);
				wclrtoeol(tuner_window->handle);
			}
//			wprintw(screen->main_window->handle, "4\n");

			if (writepass->write_status != TUNER_WRITE_STATUS_DONE)
			{
				int center = (opts->w/2);
//				int i_pos = center - (strlen(writepass->ip_addr)/2);
				mvwprintw(tuner_window->handle, start_line++, center-2, "---");
				mvwprintw(tuner_window->handle, start_line++, 0, "IP: %s", writepass->ip_addr);
				mvwprintw(tuner_window->handle, start_line++, 0, "<%s>",TUNER_WRITE_STATUS_STR[writepass->write_status]);
			}

			if (writepass->write_status == TUNER_WRITE_STATUS_WRITING)
			{
				int64_t now = time(NULL);
				rate = ((double)writepass->bytes_written/(double)(now - writepass->start_write_time)) / 100000.0;
				mvwprintw(tuner_window->handle, start_line++, 0, "Out: %0.4f %s", rate, ACTIVE_LIST[writepass->misc++]);
				if (writepass->misc > 5)
					writepass->misc = 0;
			}
			else if (writepass->write_status == TUNER_WRITE_STATUS_DONE)
			{
				uint32_t tmp[10] = {0};
//				wprintw(screen->main_window->handle, "DONE!! %d\n", i);
				tuner_window->used[i] = 0;
				i = 0;

				for(q = 0; q < 10; q++)
				{
					if (tuner_window->used[q])
						tmp[i++] = tuner_window->used[q];
				}

				if (i)
				{
					wclear(tuner_window->handle);

					for(q = 0; q < 10; q++)
					{
						tuner_window->used[q] = tmp[q];
//						wprintw(screen->main_window->handle, "MO: %d\n", tuner_window->used[q]);
					}
				}

			}
			wrefresh(tuner_window->handle);
//			pthread_mutex_unlock(&tuner_window->update_lock);
			pthread_mutex_unlock(&screen->screen_mutex);
		}
	}
}

void win_tuner_read_status(TUNER_STRUCT *tuner)
{
	if (tuner && tuner->parent && tuner->screen_var)
	{
//		DEVICE_STRUCT *device = tuner->parent;
		NCURSES_WINDOW *tuner_window = NULL;
		NCURSES_SCREEN *screen  = (NCURSES_SCREEN *)tuner->screen_var;
		NCURSES_WINDOW_OPTS *opts = NULL;

		int *p;

		if (tuner->extra_data[3] == NULL)
			tuner->extra_data[3] = calloc(1, sizeof(int));

		p = tuner->extra_data[3];

//		if (*p  == tuner->read_status && (tuner->read_status != TUNER_READ_STATUS_READING))
//			return;

		*p = tuner->read_status;

		if (tuner->extra_data[4] == NULL)
			tuner->extra_data[4] = calloc(1, sizeof(uint32_t));

		if (tuner->extra_data[5] == NULL)
			tuner->extra_data[5] = calloc(1, sizeof(size_t));

		if (tuner->extra_data[2])
		{
			int i;
			double rate;
//			size_t bytes_read = 0;
			tuner_window = tuner->extra_data[2];
			opts = &tuner_window->opts;
			int64_t now = time(NULL);
			rate = ((double)tuner->bytes_read/(double)(now - tuner->start_read_time)) / 100000.0;

			pthread_mutex_lock(&screen->screen_mutex);

//			wclear(tuner_window->handle);

			for(i=0; i < 4; i++)
			{
				wmove(tuner_window->handle, i, 0);
				wclrtoeol(tuner_window->handle);
			}

			mvwprintw(tuner_window->handle, 0, 0, "Tuner: %d <%s>", tuner_window->id, TUNER_READ_STATUS_STR[tuner->read_status]);

			if (tuner->read_status == TUNER_READ_STATUS_READING)
			{
				mvwprintw(tuner_window->handle, 1, (opts->w/2)-2, "---");
				mvwprintw(tuner_window->handle, 2, 0, "Channel: %d", tuner->tuned_channel);
				mvwprintw(tuner_window->handle, 3, 0, "In: %0.4f", rate);
			}
			wrefresh(tuner_window->handle);
			pthread_mutex_unlock(&screen->screen_mutex);
		}
	}
	else
	{
//				wprintw(screen->main_window->handle, "BLEAH!\n");

	}
}

void win_destroy_screen(NCURSES_SCREEN *screen)
{
	printw("\n\n\n\n!!!!!!!!!!!!!!!!!!win_destroy_screen 1\n");
	if (screen)
	{
		int i;
		if (screen->main_window)
		{
			printw("destroy main window\n");
			win_destroy_window(screen->main_window);
		}
		for(i = 0; i < screen->child_count; i++)
		{
			printw("destroy child window [%d]\n", i);

			win_destroy_window(screen->child_window[i]);
		}

		endwin();
	}
}
void win_destroy_window(NCURSES_WINDOW *win)
{
	if (win)
	{
//		pthread_mutex_destroy(&win->update_lock);
		delwin(win->handle);
	}
}

NCURSES_WINDOW * win_create_window(NCURSES_WINDOW_OPTS *opts)
{

	NCURSES_WINDOW *ret = NULL;
	if (opts)
	{
		ret = calloc(1, sizeof(*ret));
		if (ret)
		{
//			pthread_mutex_init(&ret->update_lock, NULL);
			ret->handle = newwin(opts->h, opts->w, opts->y , opts->x);
			memcpy(&ret->opts, opts, sizeof(*opts));
			scrollok(ret->handle, 1);
			leaveok(ret->handle, 1);
//			immedok(ret->handle, 1);
			if (opts->draw_box)
			{
				wborder(ret->handle, 0, 0, 0, 0, 0, 0, 0, 0);
				wrefresh(ret->handle);
			}
		}
	}
	return ret;
}

NCURSES_WINDOW * win_add_window(NCURSES_SCREEN *parent, NCURSES_WINDOW *window)
{
	if (parent && window)
	{
		if (parent->child_count < WIN_MAX_WINDOWS)
		{
			parent->child_window[parent->child_count++] = window;
		}
	}
	return 0;
}

void win_init(void)
{
	initscr();
//	start_color();
	curs_set(0);
	refresh();
	noecho();
	cbreak();
	leaveok(stdscr, 1);
//	keypad(stdscr, TRUE);
	nodelay(stdscr, TRUE);
}

NCURSES_SCREEN * win_init_screen(NCURSES_WINDOW_OPTS *opts)
{
	NCURSES_SCREEN *s = NULL;
	if (opts)
	{
		s = calloc(1, sizeof(*s));
		if (s)
		{
			initscr();
			refresh();
			s->main_window = win_create_window(opts);
//			refresh();
		}
	}
	return s;
}



NCURSES_SCREEN *edpvr_win_init(DEVICE_STRUCT *device)
{
	NCURSES_SCREEN * ret = NULL;
	if (device)
	{
		int i;
		TUNER_STRUCT * _tmptuner = NULL;
		NCURSES_WINDOW *tuner_window = NULL;
		NCURSES_WINDOW_OPTS w_opts = {0};
		int main_win_height = 15;
		int main_win_h_offset = 2;
		int j = 0;

		win_init();
		mvprintw(0,0, MAIN_WIN_TITLE);
		mvhline(1, 0, ACS_HLINE, strlen(MAIN_WIN_TITLE));
		mvhline(main_win_height + main_win_h_offset, 0, ACS_HLINE, COLS);
		refresh();

		w_opts.w = COLS-2;
		w_opts.h = main_win_height;
		w_opts.x = 0;
		w_opts.y = main_win_h_offset;
//		w_opts.draw_box = 0;

		ret = win_init_screen(&w_opts);

		for (i = 0; i < device->tuner_count; i++)
		{

			memset(&w_opts, 0, sizeof(NCURSES_WINDOW_OPTS));
/*
			w_opts.w = COLS-2;
			w_opts.h = 4;
			w_opts.x = 0;
			w_opts.y = 	main_win_height + main_win_h_offset  + (i*w_opts.h) + 1;
*/

			w_opts.w = TUNER_WINDOW_SZ;
			w_opts.h = LINES - (main_win_height + main_win_h_offset + 1);
			w_opts.x = (TUNER_WINDOW_SZ * i) + (i*3) + i;
			w_opts.y = 	main_win_height + main_win_h_offset + 2;

			tuner_window = win_create_window(&w_opts);
			tuner_window->id = i+1;
			win_add_window(ret, tuner_window);
			wprintw(tuner_window->handle, "Tuner: %d <none>\n", tuner_window->id);
			wrefresh(tuner_window->handle);

			if (i > 0)
				mvvline(w_opts.y, w_opts.x-2, ACS_VLINE, LINES - (main_win_height + main_win_h_offset));


			j = 0;
			_tmptuner = device->first_tuner;
			while(j < i)
			{
				_tmptuner = _tmptuner->next;
				j++;
			}

			if (_tmptuner->id == i + _tmptuner->id_offset)
			{
				tuner_window->extra_data[0] = (void *)_tmptuner;
				_tmptuner->extra_data[2] = (void*) tuner_window;
				_tmptuner->screen_var = (void*) ret;
			}
//			else
//				printw("NOOOOOO1\n");

		}

	}
	return ret;
}


int win_screen_printw(NCURSES_SCREEN *screen, const char *format, ...)
{
	if (screen && screen->main_window)
	{
		va_list ap;
		va_start(ap, format);
		pthread_mutex_lock(&screen->screen_mutex);
		vwprintw(screen->main_window->handle, format, ap);
		wrefresh(screen->main_window->handle);
//		win_vprintf(screen->main_window, format, ap);
		pthread_mutex_unlock(&screen->screen_mutex);
		va_end(ap);
	}
	return 0;
}

int win_screen_vprintw(NCURSES_SCREEN *screen, const char *format, va_list ap)
{
	if (screen && screen->main_window)
	{
		char tstr[MAX_CURSES_STR_LEN] = {0};
		vsnprintf(tstr, MAX_CURSES_STR_LEN-1, format, ap);

		pthread_mutex_lock(&screen->screen_mutex);
//		win_printf(screen->main_window, tstr);
		vwprintw(screen->main_window->handle, format, ap);
		wrefresh(screen->main_window->handle);

		pthread_mutex_unlock(&screen->screen_mutex);
	}
	return 0;
}

